package top.mcwebsite.playandroid.business.gongzhonghao.impl.vm

import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.paging.cachedIn
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import top.mcwebsite.playandroid.business.articles.api.model.ArticleModel
import top.mcwebsite.playandroid.business.gongzhonghao.impl.api.GongZhongHaoApiService
import top.mcwebsite.playandroid.business.gongzhonghao.impl.model.ChapterModel
import top.mcwebsite.playandroid.common.network.api.NetworkService
import top.mcwebsite.playandroid.common.network.api.createApi
import top.mcwebsite.playandroid.common.network.model.ApiResult
import top.mcwebsite.playandroid.lib.mvi.base.BaseViewModel
import top.mcwebsite.playandroid.lib.mvi.model.Async
import top.mcwebsite.playandroid.lib.servicemanager.core.ServiceManager

class GongZhongHaoViewModel : BaseViewModel<GongZhongHaoIntent, GongZhongHaoUiState, GongZhongHaoUiEffect>(
    defaultState = GongZhongHaoUiState()
) {

    private val apiService = ServiceManager.getService<NetworkService>()
        .createApi<GongZhongHaoApiService>()

    private var gongZhongHaoItems: List<ChapterModel>? = null

    private var gongZhongHaoPagerArray: Array<Pager<Int, ArticleModel>?>? = null
    private var pagerFlowMap = mutableMapOf<Int, Flow<PagingData<ArticleModel>>>()

    init {
        setUiState { copy(gongZhongHaoItems = Async.Loading) }
        fetchGongZhongHaoItems()
    }

    fun getPageFlow(index: Int): Flow<PagingData<ArticleModel>> {
        val items = gongZhongHaoItems ?: throw IllegalStateException()
        val itemsFlowArray = gongZhongHaoPagerArray ?: throw IllegalStateException()
        if (index !in items.indices) {
            throw IllegalStateException()
        }
        if (pagerFlowMap.containsKey(index)) {
            return pagerFlowMap[index]!!
        }
        var res = itemsFlowArray[index]
        if (res == null) {
            res = Pager(PagingConfig(10)) {
                GongZhongHaoPagingSource(items[index].id)
            }
        }
        return res.flow.cachedIn(viewModelScope).also { pagerFlowMap[index] = it }
    }

    private fun fetchGongZhongHaoItems() {
        viewModelScope.launch {
            val gongZhongHaoItems: Async<List<ChapterModel>> = when (val result = apiService.wxArticleChapters()) {
                is ApiResult.Success -> {
                    gongZhongHaoItems = result.data.data
                    gongZhongHaoPagerArray = Array(result.data.data?.size ?: 0) { null }
                    Async.Success(result.data.data ?: emptyList())
                }
                is ApiResult.Failure -> {
                    Async.Failed(result.cause.errorMsg, result.cause)
                }
            }
            setUiState { copy(gongZhongHaoItems = gongZhongHaoItems) }
        }
    }

    override fun handleIntent(intent: GongZhongHaoIntent) {
        when (intent) {
            is GongZhongHaoIntent.ClickTabItemIntent -> {
                setUiState { copy(selectedItem = intent.index) }
            }
            is GongZhongHaoIntent.PageScrollChangedIntent -> {
                setUiState { copy(selectedItem = intent.index) }
            }
            is GongZhongHaoIntent.OnClickArticleItemIntent -> {
                sendUiEffect(GongZhongHaoUiEffect.NavigationToArticleDetailPageUiEffect(intent.articleModel))
            }
        }
    }

    private inner class GongZhongHaoPagingSource(val id: Long) : PagingSource<Int, ArticleModel>() {
        override fun getRefreshKey(state: PagingState<Int, ArticleModel>): Int? {
            return null
        }

        override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ArticleModel> {
            return try {
                val nextPageNumber = params.key ?: 1
                when (val result = apiService.wxArticlesById(id, nextPageNumber)) {
                    is ApiResult.Success -> {
                        LoadResult.Page(
                            data = result.data.data?.datas ?: emptyList(),
                            prevKey = if (nextPageNumber == 1) null else nextPageNumber - 1,
                            nextKey = if (result.data.data?.over == true) null else nextPageNumber + 1
                        )
                    }
                    is ApiResult.Failure -> {
                        LoadResult.Error(result.cause)
                    }
                }
            } catch (e: Exception) {
                return LoadResult.Error(e)
            }
        }

    }
}